/**
 * 
 */
package evolution;

import java.util.ArrayList;
import java.util.TreeSet;

import ca.swank.hardquor.Move;
import ca.swank.hardquor.WalkMove;
import ca.swank.hardquor.WallMove;
import ca.swank.hq.ai.CachingSmartBoard;

/**
 * @author alonseg
 *
 */
public class ReservationTree {

	private ReservationTreeNode root;
	private CachingSmartBoard initialBoard;
	private int individualLength;
	private int turn;
	private ArrayList<ReservationTreeNode> leafs;
	
	public ReservationTree() {
		super();
		initialBoard = null;
		leafs = new ArrayList<ReservationTreeNode>();
		root = new ReservationTreeNode(null, 0, null);
	}

	public void addIndividual(Individual ind) {
		if (initialBoard == null) {
			this.initialBoard = ind.getInitialBoard();
			this.individualLength = ind.getMoveSeq().length;
			this.turn = ind.getTurn();
			//this.addAllFirstPossibleMoves();
		}
		Move[] moveSeq;
		moveSeq = ind.getMoveSeq();
		ReservationTreeNode currentNode = root;
		for (int i = 0; i < moveSeq.length; i++) {
			Move currentMove = moveSeq[i];
			ReservationTreeNode rtn = getNodeHoldingMove(currentNode, currentMove);
			if (rtn != null) {
				currentNode = rtn;
			} else {
				ReservationTreeNode tmp = new ReservationTreeNode(currentMove, 0, currentNode);
				currentNode.addChild(tmp);
				currentNode = tmp;
			}
		}
			
	}
	
	public ArrayList<Individual> GenerateIndividualsFromTree() {
		ArrayList<Individual> res = new ArrayList<Individual>();
		
		for(int i = 0; i < leafs.size(); i++) {
			ReservationTreeNode rtn = leafs.get(i);
			int height = 1;
			double lastValue = rtn.getValue();
			boolean maxHeightHasReached = false;
			Individual newInd = new Individual(individualLength, turn);
			newInd.setInitialBoard(initialBoard);
			Move[] moveseq = new Move[individualLength];
			int counter = 1;
			
			while(rtn != null) {
				if(rtn.getParent() != null)
					moveseq[individualLength - counter] = rtn.getMove();
				if ((lastValue == rtn.getValue()) && !maxHeightHasReached) {
					height++;
				} else {
					maxHeightHasReached = true;
				}
				lastValue = rtn.getValue();
				rtn = rtn.getParent();
				counter++;
			}
			newInd.setFitness(height);
			newInd.setMoveSeq(moveseq);
			res.add(newInd);
		}
		
		return res;
		
	}
	
	public void calcMinMax(BoardEvaluator be, CachingSmartBoard board) {
		getNodeMinMaxVal(root, 0, be, board);
	}
	
	private void addAllFirstPossibleMoves() {
		//handle walk moves
		TreeSet<int[]> legalWalkMoves = this.initialBoard.getLegalMoves(this.initialBoard.getPlayerLocation(turn));
		int[] tmpCurrentMove = legalWalkMoves.first(); // Temporary init value
		while(!legalWalkMoves.isEmpty()) {
			tmpCurrentMove = (int[]) legalWalkMoves.first();
			Individual in = new Individual(1, turn);
			Move[] moveseq = new Move[1]; 
			moveseq[0] = new WalkMove(this.initialBoard.getPlayerLocation(turn), tmpCurrentMove);
			in.setInitialBoard(initialBoard);
			in.setMoveSeq(moveseq);
			addIndividual(in);
			legalWalkMoves.remove(tmpCurrentMove);
		}
		//handle wall moves
		ArrayList<WallMove> walls = this.initialBoard.getAllBlockingWalls(this.initialBoard.getPlayerLocation(turn),turn);
		for(WallMove tmpCurrentWall : walls) {
			Individual in = new Individual(1, turn);
			Move[] moveseq = new Move[1]; 
			moveseq[0] = tmpCurrentWall;
			in.setInitialBoard(initialBoard);
			in.setMoveSeq(moveseq);
			addIndividual(in);
		}
		
		//TODO: handle defensive walls
	}
	
	private ReservationTreeNode getNodeMinMaxVal(ReservationTreeNode rtn, int level, BoardEvaluator be, CachingSmartBoard board) {
		if(rtn.getMove() != null)
			board.makeMove(rtn.getMove());
		if(rtn.isLeaf()) {
			leafs.add(rtn);
			rtn.setValue(be.evalBoard(board, turn));
			
			if(rtn.getMove() != null)
				board.unMove(rtn.getMove());
			return rtn;
		}
		ReservationTreeNode cur = getNodeMinMaxVal(rtn.getChildren().get(0), level + 1, be, board);
		for(int i = 1; i < rtn.getChildren().size(); i++) {
			//MAX LEVELS
			if(level % 2 == 0) {
				ReservationTreeNode tmp = getNodeMinMaxVal(rtn.getChildren().get(i), level + 1, be, board);
				if(cur.getValue() < tmp.getValue())
					cur = tmp;
			}
			//MIN LEVELS
			else {
				ReservationTreeNode tmp = getNodeMinMaxVal(rtn.getChildren().get(i), level + 1, be, board);
				if(cur.getValue() > tmp.getValue())
					cur = tmp;
			}
		}
		rtn.setValue(cur.getValue());
		if(rtn.getMove() != null)
			board.unMove(rtn.getMove());
		return cur;
	}
	
	private ReservationTreeNode getNodeHoldingMove(ReservationTreeNode t, Move m) {
		ReservationTreeNode res = null;
		if(t.isLeaf()) return res;
		for(int i = 0; i < t.getChildren().size(); i++) {
			if(t.getChildren().get(i).getMove().compareTo2(m) == 0)
				res = t.getChildren().get(i);
		}
		return res;
	}
}
